Analysis of Lung Cancer Data using CART Regression
Data Overview
Lung cancer is a serious disease that can lead to other health
conditions and death.The purpose of this dataset is to find potential
predictors of lung cancer. By identifying these risk factors, doctors
and patients can help identify high risk patients and catch cancer early
on. The response variable to be used in this analysis is, Lung
Cancer.
Predictor variables featured in this analysis are:
- Gender - Indicated as M for male and F for
female
- Age Age of patient
The remaining predictor variables are factored as 1 for NO and 2 for
YES. These variables are Coughing,Shortness of
Breath, Smoking, Yellow
Fingers, Anxiety, Peer
Pressure, Chronic Disease,
Fatigue, Allergy,
Wheezing, Alcohol Consuming,
Swallowing Difficulty, and Chest Pain.
The data was obtained using Kaggle. It has 16 variable columns and 309
observations.
Abstract
This analysis will use CART classification to find significant
predictors of the target variable, lung cancer. The CART classification
analysis found that the key factors in predicting lung cancer were
difficulty swallowing, yellow fingers, wheezing, alcohol consumption,
and allergy. However, for this analysis logistic regression performed
better than the two CART classification methods. Meaning for this
analysis, it would have been more efficient to run logistic regression
to find the best fit model to predict lung cancer. Based on the
findings, from CART classification difficulty swallowing is key
predictor in determining lung cancer and individuals who experience this
symptom, should have regular screenings with their doctor. As well,
other key predictors are allergy, alcohol consumption, and wheezing.
These symptoms should also be discussed with an individual’s doctor and
may be a precursor to or sign that an individual has lung cancer.
Introduction
This analysis seeks to answer the question of ‘What key factors can
predict lung cancer?’ This analysis can help individuals obtain early
treatment for lung cancer or allow an individual to take preventative
steps against lung cancer. Some limitations of the analysis are most
variables have been answered with a “yes” or “no”, which does not give
us the complete story. For instance, smoking is answered with a “yes” or
“no” which does not let us analyze how smoking over a period of time can
lead to lung cancer.
data <- read.csv('https://raw.githubusercontent.com/GabbyK8/Datasets/refs/heads/main/survey%20lung%20cancer.csv')
Exploratory Data Analysis
a <- ggplot(data, aes(x = SWALLOWING.DIFFICULTY, fill= LUNG_CANCER )) +
geom_bar(position = "dodge") +
labs(
x = "Difficulty Swallowing",
y = "Proportion",
fill = "Lung Cancer",
title = "Prevelance of Lung Cancer Related to Difficulty Swallowing"
) +
theme_minimal()
ggplotly(a)
ggplot(data, aes(x = LUNG_CANCER, y = AGE)) +
geom_boxplot(fill = "skyblue") +
labs(x = "Category", y = "Value", title = "Lung Cancer by Age")
CART Classification
The outcome variable is a binary variable, so this analysis will rely
on CART Classification to help predict the categorical target
variable.
# Split data into training (70%) and test (30%) sets
set.seed(123)
train.index <- createDataPartition(data$LUNG_CANCER, p = 0.7, list = FALSE)
train.data <- data[train.index, ]
test.data <- data[-train.index, ]
# Build the initial classification tree
tree.model <- rpart(LUNG_CANCER ~ .,
data = train.data,
method = "class", # classification tree
parms = list(split = "gini", # Using Gini index
#FNcost = 1, FPcost = 0.5,
loss = matrix(c(0, 0.5, 1, 0), nrow = 2)
),
control = rpart.control(minsplit = 15, # Min 15 obs to split
minbucket = 5, # Min 7 obs in leaf
# Complexity parameter
cp = 0.001, # complex parameter
maxdepth = 5))# Max tree depth
rpart.plot(tree.model,
extra = 104, # check the help document for more information
# color palette is a sequential color scheme that blends green (G) to blue (Bu)
box.palette = "GnBu",
branch.lty = 1,
shadow.col = "gray",
nn = TRUE)
# Print the complexity parameter table
pander(tree.model$cptable)
| 0.125 |
0 |
1 |
0.5 |
0.08818 |
| 0.08929 |
3 |
0.625 |
0.8929 |
0.1499 |
| 0.05357 |
4 |
0.5357 |
0.7321 |
0.1375 |
| 0.001 |
5 |
0.4821 |
0.6786 |
0.1328 |
The table above is a complexity parameter table from the decision
tree above. At the beginning of the tree there are zero splits with a
training error of 0, which is our baseline. As the tree grows there are
more splits and a decreased relative error, suggesting a better fit on
training data. Ideally, we want the smallest subtree where xerror is
within 1 SE of the minimum xerror. The minimum xerror is 0.6786, shown
in the last row. Using the 1 SE rule, our threshold would be the minimum
xerror plus it’s standard deviation which would be 0.8114. The first row
to meet this rule is the third row which has 4 splits and a CP of
0.05357, because it’s xerror is 0.7321 which is less than 0.8114.This
means the tree should be pruned to 4 splits for model simplicity and
accuracy.
# Plot the cross-validation results
plotcp(tree.model)
Pruning
# Find the optimal cp value that minimizes cross-validated error
min.cp <- tree.model$cptable[which.min(tree.model$cptable[,"xerror"]),"CP"]
# Prune the tree using the optimal cp
pruned.tree.1SE <- prune(tree.model, cp = 0.0073)
pruned.tree.min <- prune(tree.model, cp = min.cp)
# Visualize the pruned tree
rpart.plot(pruned.tree.1SE,
extra = 104, # check the help document for more information
# color palette is a sequential color scheme that blends green (G) to blue (Bu)
box.palette = "GnBu",
branch.lty = 1,
shadow.col = "gray",
nn = TRUE)
# Visualize the pruned tree
rpart.plot(pruned.tree.min,
extra = 104, # check the help document for more information
# color palette is a sequential color scheme that blends green (G) to blue (Bu)
box.palette = "GnBu",
branch.lty = 1,
shadow.col = "gray",
nn = TRUE)
# Make predictions on the test set
test.data$LUNG_CANCER<- as.factor(test.data$LUNG_CANCER)
train.data$LUNG_CANCER<- as.factor(train.data$LUNG_CANCER)
pred.label.1SE <- predict(pruned.tree.1SE, test.data, type = "class") # default cutoff 0.5
pred.prob.1SE <- predict(pruned.tree.1SE, test.data, type = "prob")[,2]
##
pred.label.min <- predict(pruned.tree.min, test.data, type = "class") # default cutoff 0.5
pred.prob.min <- predict(pruned.tree.min, test.data, type = "prob")[,2]
# Confusion matrix
#conf.matrix <- confusionMatrix(pred.label, test.data$diabetes, positive = "pos")
#print(conf.matrix)
########################
### logistic regression
logit.fit <- glm(LUNG_CANCER ~ ., data = train.data, family = binomial)
AIC.logit <- step(logit.fit, direction = "both", trace = 0)
pred.logit <- predict(AIC.logit, test.data, type = "response")
# ROC curve and AUC
roc.tree.1SE <- roc(test.data$LUNG_CANCER, pred.prob.1SE)
roc.tree.min <- roc(test.data$LUNG_CANCER, pred.prob.min)
roc.logit <- roc(test.data$LUNG_CANCER, pred.logit)
##
### Sen-Spe
tree.1SE.sen <- roc.tree.1SE$sensitivities
tree.1SE.spe <- roc.tree.1SE$specificities
#
tree.min.sen <- roc.tree.min$sensitivities
tree.min.spe <- roc.tree.min$specificities
#
logit.sen <- roc.logit$sensitivities
logit.spe <- roc.logit$specificities
## AUC
auc.tree.1SE <- roc.tree.1SE$auc
auc.tree.min <- roc.tree.min$auc
auc.logit <- roc.logit$auc
###
plot(1-logit.spe, logit.sen,
xlab = "1 - specificity",
ylab = "sensitivity",
col = "darkred",
type = "l",
lty = 1,
lwd = 1,
main = "ROC: CART and Logistic Regression")
lines(1-tree.1SE.spe, tree.1SE.sen,
col = "blue",
lty = 1,
lwd = 1)
lines(1-tree.min.spe, tree.min.sen,
col = "orange",
lty = 1,
lwd = 1)
abline(0,1, col = "skyblue3", lty = 2, lwd = 2)
legend("bottomright", c("Logistic", "Tree 1SE", "Tree Min"),
lty = c(1,1,1), lwd = rep(1,3),
col = c("red", "blue", "orange"),
bty="n",cex = 0.8)
## annotation - AUC
text(0.8, 0.46, paste("Logistic AUC: ", round(auc.logit,4)), cex = 0.8)
text(0.8, 0.4, paste("Tree 1SE AUC: ", round(auc.tree.1SE,4)), cex = 0.8)
text(0.8, 0.34, paste("Tree Min AUC: ", round(auc.tree.min,4)), cex = 0.8)
Optimal Cutoff
# preditive probabilities of tree.min model.
pred.prob.min <- predict(pruned.tree.min, train.data, type = "prob")[,2]
##
cost <- NULL
cutoff <-seq(0,1, length = 10)
##
for (i in 1:10){
pred.label <- ifelse(pred.prob.min > cutoff[i], "YES", "NO")
FN <- sum(pred.label == "NO" & train.data$LUNG_CANCER == "YES")
FP <- sum(pred.label == "YES" & train.data$LUNG_CANCER == "NO")
cost[i] = 5*FP + 20*FN
}
## identify optimal cut-off
min.ID <- which(cost == min(cost)) # could have multiple minimum
optim.prob <- mean(cutoff[min.ID]) # take the average of the cut-offs
##
plot(cutoff, cost, type = "b", col = "navy",
main = "Cutoff vs Misclassification Cost")
##
text(0.2, 3500, paste("Optimal cutoff:", round(optim.prob,4)), cex = 0.8)
Conclusion
The logistic regression model performs superior to the suggested CART
Classification models. However, the CART models showed that difficulty
breathing was a key predictor in lung cancer. Other key predictors were
alcohol consumption, wheezing, and allergy.
LS0tCnRpdGxlOiAiUHJvamVjdCAzIgphdXRob3I6ICJHYWJyaWVsbGEgS2hhbGlsIgpkYXRlOiAiMjAyNS0wNC0yOSIKb3V0cHV0OgoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIHRoZW1lOiBsdW1lbgogIHdvcmRfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAga2VlcF9tZDogeWVzCiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogbm8KICAgIGZpZ193aWR0aDogMwogICAgZmlnX2hlaWdodDogMwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHs9aHRtbH0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CgovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8KCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovCiAgZm9udC1zaXplOiAyMnB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7Cn0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLwogIGZvbnQtc2l6ZTogMThweDsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBuYXZ5OwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwogIGZvbnQtd2VpZ2h0OiBib2xkOwp9CmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDIycHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLwogICAgZm9udC1zaXplOiAyMHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7CiAgICBmb250LXdlaWdodDogYm9sZDsKfQoKaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9Cgpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQoKLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0KCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9Cgo8L3N0eWxlPgpgYGAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLgppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQogICBsaWJyYXJ5KGtuaXRyKQp9CmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKbGlicmFyeSh0aWR5dmVyc2UpCn0KaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQpsaWJyYXJ5KEdHYWxseSkKfQoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHBhbmRlcikKIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShycGFydCkgICAgICAgICMgRm9yIGRlY2lzaW9uIHRyZWVzCmxpYnJhcnkocnBhcnQucGxvdCkgICAjIEZvciB2aXN1YWxpemluZyB0cmVlcwpsaWJyYXJ5KGNhcmV0KSAgICAgICAgIyBGb3IgbW9kZWwgZXZhbHVhdGlvbgpsaWJyYXJ5KHBST0MpICAgICAgICAgIyBGb3IgUk9DIGFuYWx5c2lzCgprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLgogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAKCmBgYAojIEFuYWx5c2lzIG9mIEx1bmcgQ2FuY2VyIERhdGEgdXNpbmcgQ0FSVCBSZWdyZXNzaW9uCgojIyBEYXRhIE92ZXJ2aWV3CgpMdW5nIGNhbmNlciBpcyBhIHNlcmlvdXMgZGlzZWFzZSB0aGF0IGNhbiBsZWFkIHRvIG90aGVyIGhlYWx0aCBjb25kaXRpb25zIGFuZCBkZWF0aC5UaGUgcHVycG9zZSBvZiB0aGlzIGRhdGFzZXQgaXMgdG8gZmluZCBwb3RlbnRpYWwgcHJlZGljdG9ycyBvZiBsdW5nIGNhbmNlci4gQnkgaWRlbnRpZnlpbmcgdGhlc2UgcmlzayBmYWN0b3JzLCBkb2N0b3JzIGFuZCBwYXRpZW50cyBjYW4gaGVscCBpZGVudGlmeSBoaWdoIHJpc2sgcGF0aWVudHMgYW5kIGNhdGNoIGNhbmNlciBlYXJseSBvbi4gVGhlIHJlc3BvbnNlIHZhcmlhYmxlIHRvIGJlIHVzZWQgaW4gdGhpcyBhbmFseXNpcyBpcywgKipMdW5nIENhbmNlcioqLgoKUHJlZGljdG9yIHZhcmlhYmxlcyBmZWF0dXJlZCBpbiB0aGlzIGFuYWx5c2lzIGFyZToKCi0gICAqKkdlbmRlcioqIC0gSW5kaWNhdGVkIGFzIE0gZm9yIG1hbGUgYW5kIEYgZm9yIGZlbWFsZQotICAgKipBZ2UqKiBBZ2Ugb2YgcGF0aWVudAoKVGhlIHJlbWFpbmluZyBwcmVkaWN0b3IgdmFyaWFibGVzIGFyZSBmYWN0b3JlZCBhcyAxIGZvciBOTyBhbmQgMiBmb3IgWUVTLiBUaGVzZSB2YXJpYWJsZXMgYXJlICoqQ291Z2hpbmcqKiwqKlNob3J0bmVzcyBvZiBCcmVhdGgqKiwgKipTbW9raW5nKiosICoqWWVsbG93IEZpbmdlcnMqKiwgKipBbnhpZXR5KiosICoqUGVlciBQcmVzc3VyZSoqLCAqKkNocm9uaWMgRGlzZWFzZSoqLCAqKkZhdGlndWUqKiwgKipBbGxlcmd5KiosICoqV2hlZXppbmcqKiwgKipBbGNvaG9sIENvbnN1bWluZyoqLCAqKlN3YWxsb3dpbmcgRGlmZmljdWx0eSoqLCBhbmQgKipDaGVzdCBQYWluKiouClRoZSBkYXRhIHdhcyBvYnRhaW5lZCB1c2luZyBLYWdnbGUuIEl0IGhhcyAxNiB2YXJpYWJsZSBjb2x1bW5zIGFuZCAzMDkgb2JzZXJ2YXRpb25zLgoKIyMgQWJzdHJhY3QKClRoaXMgYW5hbHlzaXMgd2lsbCB1c2UgQ0FSVCBjbGFzc2lmaWNhdGlvbiB0byBmaW5kIHNpZ25pZmljYW50IHByZWRpY3RvcnMgb2YgdGhlIHRhcmdldCB2YXJpYWJsZSwgbHVuZyBjYW5jZXIuIFRoZSBDQVJUIGNsYXNzaWZpY2F0aW9uIGFuYWx5c2lzIGZvdW5kIHRoYXQgdGhlIGtleSBmYWN0b3JzIGluIHByZWRpY3RpbmcgbHVuZyBjYW5jZXIgd2VyZSBkaWZmaWN1bHR5IHN3YWxsb3dpbmcsIHllbGxvdyBmaW5nZXJzLCB3aGVlemluZywgYWxjb2hvbCBjb25zdW1wdGlvbiwgYW5kIGFsbGVyZ3kuIEhvd2V2ZXIsIGZvciB0aGlzIGFuYWx5c2lzIGxvZ2lzdGljIHJlZ3Jlc3Npb24gcGVyZm9ybWVkIGJldHRlciB0aGFuIHRoZSB0d28gQ0FSVCBjbGFzc2lmaWNhdGlvbiBtZXRob2RzLiBNZWFuaW5nIGZvciB0aGlzIGFuYWx5c2lzLCBpdCB3b3VsZCBoYXZlIGJlZW4gbW9yZSBlZmZpY2llbnQgdG8gcnVuIGxvZ2lzdGljIHJlZ3Jlc3Npb24gdG8gZmluZCB0aGUgYmVzdCBmaXQgbW9kZWwgdG8gcHJlZGljdCBsdW5nIGNhbmNlci4gQmFzZWQgb24gdGhlIGZpbmRpbmdzLCBmcm9tIENBUlQgY2xhc3NpZmljYXRpb24gZGlmZmljdWx0eSBzd2FsbG93aW5nIGlzIGtleSBwcmVkaWN0b3IgaW4gZGV0ZXJtaW5pbmcgbHVuZyBjYW5jZXIgYW5kIGluZGl2aWR1YWxzIHdobyBleHBlcmllbmNlIHRoaXMgc3ltcHRvbSwgc2hvdWxkIGhhdmUgcmVndWxhciBzY3JlZW5pbmdzIHdpdGggdGhlaXIgZG9jdG9yLiBBcyB3ZWxsLCBvdGhlciBrZXkgcHJlZGljdG9ycyBhcmUgYWxsZXJneSwgYWxjb2hvbCBjb25zdW1wdGlvbiwgYW5kIHdoZWV6aW5nLiBUaGVzZSBzeW1wdG9tcyBzaG91bGQgYWxzbyBiZSBkaXNjdXNzZWQgd2l0aCBhbiBpbmRpdmlkdWFsJ3MgZG9jdG9yIGFuZCBtYXkgYmUgYSBwcmVjdXJzb3IgdG8gb3Igc2lnbiB0aGF0IGFuIGluZGl2aWR1YWwgaGFzIGx1bmcgY2FuY2VyLgoKIyMgSW50cm9kdWN0aW9uCgpUaGlzIGFuYWx5c2lzIHNlZWtzIHRvIGFuc3dlciB0aGUgcXVlc3Rpb24gb2YgJ1doYXQga2V5IGZhY3RvcnMgY2FuIHByZWRpY3QgbHVuZyBjYW5jZXI/JyBUaGlzIGFuYWx5c2lzIGNhbiBoZWxwIGluZGl2aWR1YWxzIG9idGFpbiBlYXJseSB0cmVhdG1lbnQgZm9yIGx1bmcgY2FuY2VyIG9yIGFsbG93IGFuIGluZGl2aWR1YWwgdG8gdGFrZSBwcmV2ZW50YXRpdmUgc3RlcHMgYWdhaW5zdCBsdW5nIGNhbmNlci4gU29tZSBsaW1pdGF0aW9ucyBvZiB0aGUgYW5hbHlzaXMgYXJlIG1vc3QgdmFyaWFibGVzIGhhdmUgYmVlbiBhbnN3ZXJlZCB3aXRoIGEgInllcyIgb3IgIm5vIiwgd2hpY2ggZG9lcyBub3QgZ2l2ZSB1cyB0aGUgY29tcGxldGUgc3RvcnkuIEZvciBpbnN0YW5jZSwgc21va2luZyBpcyBhbnN3ZXJlZCB3aXRoIGEgInllcyIgb3IgIm5vIiB3aGljaCBkb2VzIG5vdCBsZXQgdXMgYW5hbHl6ZSBob3cgc21va2luZyBvdmVyIGEgcGVyaW9kIG9mIHRpbWUgY2FuIGxlYWQgdG8gbHVuZyBjYW5jZXIuCgpgYGB7cn0KZGF0YSA8LSByZWFkLmNzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0dhYmJ5SzgvRGF0YXNldHMvcmVmcy9oZWFkcy9tYWluL3N1cnZleSUyMGx1bmclMjBjYW5jZXIuY3N2JykKCgpgYGAKCiMjIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhpcyBmaWd1cmUgc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpZmZpY3VsdHkgc3dhbGxvd2luZyBhbmQgbHVuZyBjYW5jZXIuIEJhc2VkIG9uIHRoZSBncmFwaCwgdGhlcmUgYXJlIG1vcmUgcGVvcGxlIHdpdGggbHVuZyBjYW5jZXIgdGhhdCBzbW9rZSB0aGFuIGZvciB0aG9zZSB3aG8gZG8gbm90IHNtb2tlLiBNZWFuaW5nIHRoZXJlIGlzIGEgcG9zc2libGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZGlmZmljdWx0eSBzd2FsbG93aW5nIGFuZCBiZWluZyBkaWFnbm9zZWQgd2l0aCBsdW5nIGNhbmNlci4nIH0KCmEgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gU1dBTExPV0lORy5ESUZGSUNVTFRZLCBmaWxsPSBMVU5HX0NBTkNFUiApKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgbGFicygKICAgIHggPSAiRGlmZmljdWx0eSBTd2FsbG93aW5nIiwKICAgIHkgPSAiUHJvcG9ydGlvbiIsCiAgICBmaWxsID0gIkx1bmcgQ2FuY2VyIiwKICAgIHRpdGxlID0gIlByZXZlbGFuY2Ugb2YgTHVuZyBDYW5jZXIgUmVsYXRlZCB0byBEaWZmaWN1bHR5IFN3YWxsb3dpbmciCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmdncGxvdGx5KGEpCmBgYAoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGUgYm94cGxvdCBzaG93cyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYWdlIGFuZCBsdW5nIGNhbmNlci4gSXQgc2hvd3MgdGhhdCBtb3N0IHBlb3BsZSB3aG8gYXJlIGRpYWdub3NlZCB3aXRoIGx1bmcgY2FuY2VyIGFyZSBvbGRlci4gU3VnZ2VzdGluZyBhIGNvcnJlbGF0aW9uIGJldHdlZW4gYWdlIGFuZCBsdW5nIGNhbmNlciBkaWFnbm9zaXMuJ30KZ2dwbG90KGRhdGEsIGFlcyh4ID0gTFVOR19DQU5DRVIsIHkgPSBBR0UpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAic2t5Ymx1ZSIpICsKICBsYWJzKHggPSAiQ2F0ZWdvcnkiLCB5ID0gIlZhbHVlIiwgdGl0bGUgPSAiTHVuZyBDYW5jZXIgYnkgQWdlIikKCgoKCmBgYAoKIyMgQ0FSVCBDbGFzc2lmaWNhdGlvbgoKVGhlIG91dGNvbWUgdmFyaWFibGUgaXMgYSBiaW5hcnkgdmFyaWFibGUsIHNvIHRoaXMgYW5hbHlzaXMgd2lsbCByZWx5IG9uIENBUlQgQ2xhc3NpZmljYXRpb24gdG8gaGVscCBwcmVkaWN0IHRoZSBjYXRlZ29yaWNhbCB0YXJnZXQgdmFyaWFibGUuIApgYGB7cn0KCgoKCgojIFNwbGl0IGRhdGEgaW50byB0cmFpbmluZyAoNzAlKSBhbmQgdGVzdCAoMzAlKSBzZXRzCnNldC5zZWVkKDEyMykKdHJhaW4uaW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihkYXRhJExVTkdfQ0FOQ0VSLCBwID0gMC43LCBsaXN0ID0gRkFMU0UpCnRyYWluLmRhdGEgPC0gZGF0YVt0cmFpbi5pbmRleCwgXQp0ZXN0LmRhdGEgPC0gZGF0YVstdHJhaW4uaW5kZXgsIF0KCiMgQnVpbGQgdGhlIGluaXRpYWwgY2xhc3NpZmljYXRpb24gdHJlZQp0cmVlLm1vZGVsIDwtIHJwYXJ0KExVTkdfQ0FOQ0VSIH4gLiwgCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImNsYXNzIiwgICAjIGNsYXNzaWZpY2F0aW9uIHRyZWUKICAgICAgICAgICAgICAgICAgICBwYXJtcyA9IGxpc3Qoc3BsaXQgPSAiZ2luaSIsICAjIFVzaW5nIEdpbmkgaW5kZXgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI0ZOY29zdCA9IDEsIEZQY29zdCA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9zcyA9IG1hdHJpeChjKDAsIDAuNSwgMSwgMCksIG5yb3cgPSAyKSAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2wobWluc3BsaXQgPSAxNSwgICMgTWluIDE1IG9icyB0byBzcGxpdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluYnVja2V0ID0gNSwgICAjIE1pbiA3IG9icyBpbiBsZWFmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIENvbXBsZXhpdHkgcGFyYW1ldGVyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcCA9IDAuMDAxLCAjIGNvbXBsZXggcGFyYW1ldGVyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhkZXB0aCA9IDUpKSMgTWF4IHRyZWUgZGVwdGgKCgoKYGBgCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTcsIGZpZy5jYXA9J1RoZSB0cmVlIGNsYXNzaWZpY2F0aW9uIHNob3dzIHRoZSBmaXJzdCBzcGxpdCBhdCBzd2FsbG93aW5nIGRpZmZpY3VsdHksIGluZGljYXRpbmcgaXQgd2FzIHRoZSBtb3N0IHNpZ25pZmljYW50IHZhcmlhYmxlIG9mIHRoZSBzcGxpdC4gT3ZlcmFsbCwgaXQgc2hvd3MgdGhhdCBpbmRpdmlkdWFscyB3aXRoIGRpZmZpY3VsdHkgc3dhbGxvd2luZyBhbmQgYWxsZXJneSBoYXZlIGEgaGlnaCBjaGFuY2Ugb2YgZ2V0dGluZyBsdW5nIGNhbmNlci4gSXQgYWxzbyBzdWdnZXN0cyB0aGF0IGV2ZW4gd2l0aG91dCBhbGxlcmd5LCB3aGVlemluZywgYWxjb2hvbCBjb25zdW1wdGlvbiwgYW5kIHllbGxvdyBmaW5nZXJzIGNhbiByYWlzZSBwcm9iYWJpbGl0eSBvZiBnZXR0aW5nIGRpYWdub3NlZCB3aXRoIGx1bmcgY2FuY2VyLid9CnJwYXJ0LnBsb3QodHJlZS5tb2RlbCwgCiAgICAgICAgICAgZXh0cmEgPSAxMDQsICMgY2hlY2sgdGhlIGhlbHAgZG9jdW1lbnQgZm9yIG1vcmUgaW5mb3JtYXRpb24KICAgICAgICAgICAjIGNvbG9yIHBhbGV0dGUgaXMgYSBzZXF1ZW50aWFsIGNvbG9yIHNjaGVtZSB0aGF0IGJsZW5kcyBncmVlbiAoRykgdG8gYmx1ZSAoQnUpCiAgICAgICAgICAgYm94LnBhbGV0dGUgPSAiR25CdSIsICAKICAgICAgICAgICBicmFuY2gubHR5ID0gMSwgCiAgICAgICAgICAgc2hhZG93LmNvbCA9ICJncmF5IiwgCiAgICAgICAgICAgbm4gPSBUUlVFKQoKCmBgYAoKYGBge3IgfQojIFByaW50IHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciB0YWJsZQpwYW5kZXIodHJlZS5tb2RlbCRjcHRhYmxlKQoKYGBgCgpUaGUgdGFibGUgYWJvdmUgaXMgYSBjb21wbGV4aXR5IHBhcmFtZXRlciB0YWJsZSBmcm9tIHRoZSBkZWNpc2lvbiB0cmVlIGFib3ZlLiBBdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSB0cmVlIHRoZXJlIGFyZSB6ZXJvIHNwbGl0cyB3aXRoIGEgdHJhaW5pbmcgZXJyb3Igb2YgMCwgd2hpY2ggaXMgb3VyIGJhc2VsaW5lLiBBcyB0aGUgdHJlZSBncm93cyB0aGVyZSBhcmUgbW9yZSBzcGxpdHMgYW5kIGEgZGVjcmVhc2VkIHJlbGF0aXZlIGVycm9yLCBzdWdnZXN0aW5nIGEgYmV0dGVyIGZpdCBvbiB0cmFpbmluZyBkYXRhLiBJZGVhbGx5LCB3ZSB3YW50IHRoZSBzbWFsbGVzdCBzdWJ0cmVlIHdoZXJlIHhlcnJvciBpcyB3aXRoaW4gMSBTRSBvZiB0aGUgbWluaW11bSB4ZXJyb3IuIFRoZSBtaW5pbXVtIHhlcnJvciBpcyAwLjY3ODYsIHNob3duIGluIHRoZSBsYXN0IHJvdy4gVXNpbmcgdGhlIDEgU0UgcnVsZSwgb3VyIHRocmVzaG9sZCB3b3VsZCBiZSB0aGUgbWluaW11bSB4ZXJyb3IgcGx1cyBpdCdzIHN0YW5kYXJkIGRldmlhdGlvbiB3aGljaCB3b3VsZCBiZSAwLjgxMTQuIFRoZSBmaXJzdCByb3cgdG8gbWVldCB0aGlzIHJ1bGUgaXMgdGhlIHRoaXJkIHJvdyB3aGljaCBoYXMgNCBzcGxpdHMgYW5kIGEgQ1Agb2YgMC4wNTM1NywgYmVjYXVzZSBpdCdzIHhlcnJvciBpcyAwLjczMjEgd2hpY2ggaXMgbGVzcyB0aGFuIDAuODExNC5UaGlzIG1lYW5zIHRoZSB0cmVlIHNob3VsZCBiZSBwcnVuZWQgdG8gNCBzcGxpdHMgZm9yIG1vZGVsIHNpbXBsaWNpdHkgYW5kIGFjY3VyYWN5LgoKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhlIHBsb3QgYWJvdmUgc2hvd3MgdGhlIGNvcnNzLXZhbGlkYXJpb24gZXJyb3IgKHJlbCBlcnJvcikgdnMgdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgZnJvbSB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbC4gVGhlIGhvcnJpem9udGFsIGRhc2hlZCBsaW5lIHNob3dzIHRoZSBtaW5pbXVtIGNyb3NzLXZhbGlkYXRpb24gZXJyb3IgKyAxU0UuIFRoZSAxLVNFIHJ1bGUgc3VnZ2VzdHMgY2hvb3NpbmcgdGhlIHNpbXBsZXN0IG1vZGVsIHdpdGhpbiAxIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBsb3dlc3QgY3Jvc3MtdmFsaWRhdGlvbiBlcnJvci4gVGhlIGRhc2hlZCBsaW5lIGNyb3NzZXMgdGhlIGVycm9yIGJhciBvZiB0aGUgdHJlZSBzaXplIDEsIG1lYW5pbmcgdGhpcyBpcyB0aGUgc2ltcGxlc3QgbW9kZWwgdGhhdCB3aWxsIHN0aWxsIHBlcmZvcm0gYWRlcXVhdGVseS4gT3VyIG9wdGltYWwgY3Agd291bGQgYmUgMC4wMDczLCB3aGljaCBpcyBhdCA2IHNwbGl0cy4nfQojIFBsb3QgdGhlIGNyb3NzLXZhbGlkYXRpb24gcmVzdWx0cwpwbG90Y3AodHJlZS5tb2RlbCkKCmBgYAoKIyMgUHJ1bmluZwoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD03LCBmaWcuY2FwPSdUaGUgYWJvdmUgbW9kZWwgc2hvd3MgdGhlIHRyZWUgYWZ0ZXIgcHJ1bmluZy4gVGhpcyByZXN1bHQgaXMgdGhlIHNhbWUgYXMgb3VyIGZpcnN0IG1vZGVsLCBzaW5jZSB0aGUgb3B0aW1hbCBjcCBpcyBhcyA2IHNwbGl0cy4nfQojIEZpbmQgdGhlIG9wdGltYWwgY3AgdmFsdWUgdGhhdCBtaW5pbWl6ZXMgY3Jvc3MtdmFsaWRhdGVkIGVycm9yCm1pbi5jcCA8LSB0cmVlLm1vZGVsJGNwdGFibGVbd2hpY2gubWluKHRyZWUubW9kZWwkY3B0YWJsZVssInhlcnJvciJdKSwiQ1AiXQoKIyBQcnVuZSB0aGUgdHJlZSB1c2luZyB0aGUgb3B0aW1hbCBjcApwcnVuZWQudHJlZS4xU0UgPC0gcHJ1bmUodHJlZS5tb2RlbCwgY3AgPSAwLjAwNzMpICAKcHJ1bmVkLnRyZWUubWluIDwtIHBydW5lKHRyZWUubW9kZWwsIGNwID0gbWluLmNwKQoKIyBWaXN1YWxpemUgdGhlIHBydW5lZCB0cmVlCnJwYXJ0LnBsb3QocHJ1bmVkLnRyZWUuMVNFLCAKICAgICAgICAgICBleHRyYSA9IDEwNCwgIyBjaGVjayB0aGUgaGVscCBkb2N1bWVudCBmb3IgbW9yZSBpbmZvcm1hdGlvbgogICAgICAgICAgICMgY29sb3IgcGFsZXR0ZSBpcyBhIHNlcXVlbnRpYWwgY29sb3Igc2NoZW1lIHRoYXQgYmxlbmRzIGdyZWVuIChHKSB0byBibHVlIChCdSkKICAgICAgICAgICBib3gucGFsZXR0ZSA9ICJHbkJ1IiwgIAogICAgICAgICAgIGJyYW5jaC5sdHkgPSAxLCAKICAgICAgICAgICBzaGFkb3cuY29sID0gImdyYXkiLCAKICAgICAgICAgICBubiA9IFRSVUUpCgpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhpcyBpcyB0aGUgZmluYWwgcHJ1bmVkIHRyZWUgYmFzZWQgb24gdGhlIHBsb3Qgb2YgY3Jvc3MtdmFsaWRhdGlvbiBlcnJvciB2cyBDUC4gVGhpcyBzaG93cyB0aGF0IHN3YWxsb3dpbmcgZGlmZmljdWx0eSBpcyBzaWduaWZpY2FudCBhbG9uZSBpbiBwcmVkaWN0aW5nIGx1bmcgY2FuY2VyLid9CiMgVmlzdWFsaXplIHRoZSBwcnVuZWQgdHJlZQpycGFydC5wbG90KHBydW5lZC50cmVlLm1pbiwgCiAgICAgICAgICAgZXh0cmEgPSAxMDQsICMgY2hlY2sgdGhlIGhlbHAgZG9jdW1lbnQgZm9yIG1vcmUgaW5mb3JtYXRpb24KICAgICAgICAgICAjIGNvbG9yIHBhbGV0dGUgaXMgYSBzZXF1ZW50aWFsIGNvbG9yIHNjaGVtZSB0aGF0IGJsZW5kcyBncmVlbiAoRykgdG8gYmx1ZSAoQnUpCiAgICAgICAgICAgYm94LnBhbGV0dGUgPSAiR25CdSIsICAKICAgICAgICAgICBicmFuY2gubHR5ID0gMSwgCiAgICAgICAgICAgc2hhZG93LmNvbCA9ICJncmF5IiwgCiAgICAgICAgICAgbm4gPSBUUlVFKQoKYGBgCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTUsIGZpZy5jYXA9J1RoZSBwbG90IGNvbXBhcmVzIGxvZ2lzdGljIHJlZ3Jlc3Npb24gYW5kIHRoZSBwcnVuZWQgVHJlZSBtb2RlbHMuIEJhc2VkIG9uIHRoZSBBVUMgYW5kIFJPQywgbG9naXN0aWMgcmVncmVzc2lvbiBwZXJmb3JtcyBiZXR0ZXIgdGhhbiB0aGUgb3RoZXIgdHdvIG1vZGVscy4nfQojIE1ha2UgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0CnRlc3QuZGF0YSRMVU5HX0NBTkNFUjwtIGFzLmZhY3Rvcih0ZXN0LmRhdGEkTFVOR19DQU5DRVIpCnRyYWluLmRhdGEkTFVOR19DQU5DRVI8LSBhcy5mYWN0b3IodHJhaW4uZGF0YSRMVU5HX0NBTkNFUikKCnByZWQubGFiZWwuMVNFIDwtIHByZWRpY3QocHJ1bmVkLnRyZWUuMVNFLCB0ZXN0LmRhdGEsIHR5cGUgPSAiY2xhc3MiKSAjIGRlZmF1bHQgY3V0b2ZmIDAuNQpwcmVkLnByb2IuMVNFIDwtIHByZWRpY3QocHJ1bmVkLnRyZWUuMVNFLCB0ZXN0LmRhdGEsIHR5cGUgPSAicHJvYiIpWywyXQojIwpwcmVkLmxhYmVsLm1pbiA8LSBwcmVkaWN0KHBydW5lZC50cmVlLm1pbiwgdGVzdC5kYXRhLCB0eXBlID0gImNsYXNzIikgIyBkZWZhdWx0IGN1dG9mZiAwLjUKcHJlZC5wcm9iLm1pbiA8LSBwcmVkaWN0KHBydW5lZC50cmVlLm1pbiwgdGVzdC5kYXRhLCB0eXBlID0gInByb2IiKVssMl0KCiMgQ29uZnVzaW9uIG1hdHJpeAojY29uZi5tYXRyaXggPC0gY29uZnVzaW9uTWF0cml4KHByZWQubGFiZWwsIHRlc3QuZGF0YSRkaWFiZXRlcywgcG9zaXRpdmUgPSAicG9zIikKI3ByaW50KGNvbmYubWF0cml4KQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyAgbG9naXN0aWMgcmVncmVzc2lvbgpsb2dpdC5maXQgPC0gZ2xtKExVTkdfQ0FOQ0VSIH4gLiwgZGF0YSA9IHRyYWluLmRhdGEsIGZhbWlseSA9IGJpbm9taWFsKQpBSUMubG9naXQgPC0gc3RlcChsb2dpdC5maXQsIGRpcmVjdGlvbiA9ICJib3RoIiwgdHJhY2UgPSAwKQpwcmVkLmxvZ2l0IDwtIHByZWRpY3QoQUlDLmxvZ2l0LCB0ZXN0LmRhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQoKIyBST0MgY3VydmUgYW5kIEFVQwpyb2MudHJlZS4xU0UgPC0gcm9jKHRlc3QuZGF0YSRMVU5HX0NBTkNFUiwgcHJlZC5wcm9iLjFTRSkKcm9jLnRyZWUubWluIDwtIHJvYyh0ZXN0LmRhdGEkTFVOR19DQU5DRVIsIHByZWQucHJvYi5taW4pCnJvYy5sb2dpdCA8LSByb2ModGVzdC5kYXRhJExVTkdfQ0FOQ0VSLCBwcmVkLmxvZ2l0KQoKIyMKIyMjIFNlbi1TcGUKdHJlZS4xU0Uuc2VuIDwtIHJvYy50cmVlLjFTRSRzZW5zaXRpdml0aWVzCnRyZWUuMVNFLnNwZSA8LSByb2MudHJlZS4xU0Ukc3BlY2lmaWNpdGllcwojCnRyZWUubWluLnNlbiA8LSByb2MudHJlZS5taW4kc2Vuc2l0aXZpdGllcwp0cmVlLm1pbi5zcGUgPC0gcm9jLnRyZWUubWluJHNwZWNpZmljaXRpZXMKIwpsb2dpdC5zZW4gPC0gcm9jLmxvZ2l0JHNlbnNpdGl2aXRpZXMKbG9naXQuc3BlIDwtIHJvYy5sb2dpdCRzcGVjaWZpY2l0aWVzCiMjIEFVQwphdWMudHJlZS4xU0UgPC0gcm9jLnRyZWUuMVNFJGF1YwphdWMudHJlZS5taW4gPC0gcm9jLnRyZWUubWluJGF1YwphdWMubG9naXQgPC0gcm9jLmxvZ2l0JGF1YwojIyMKcGxvdCgxLWxvZ2l0LnNwZSwgbG9naXQuc2VuLCAgCiAgICAgeGxhYiA9ICIxIC0gc3BlY2lmaWNpdHkiLAogICAgIHlsYWIgPSAic2Vuc2l0aXZpdHkiLAogICAgIGNvbCA9ICJkYXJrcmVkIiwKICAgICB0eXBlID0gImwiLAogICAgIGx0eSA9IDEsCiAgICAgbHdkID0gMSwKICAgICBtYWluID0gIlJPQzogQ0FSVCBhbmQgTG9naXN0aWMgUmVncmVzc2lvbiIpCmxpbmVzKDEtdHJlZS4xU0Uuc3BlLCB0cmVlLjFTRS5zZW4sIAogICAgICBjb2wgPSAiYmx1ZSIsCiAgICAgIGx0eSA9IDEsCiAgICAgIGx3ZCA9IDEpCmxpbmVzKDEtdHJlZS5taW4uc3BlLCB0cmVlLm1pbi5zZW4sICAgICAgCiAgICAgIGNvbCA9ICJvcmFuZ2UiLAogICAgICBsdHkgPSAxLAogICAgICBsd2QgPSAxKQphYmxpbmUoMCwxLCBjb2wgPSAic2t5Ymx1ZTMiLCBsdHkgPSAyLCBsd2QgPSAyKQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgYygiTG9naXN0aWMiLCAiVHJlZSAxU0UiLCAiVHJlZSBNaW4iKSwKICAgICAgIGx0eSA9IGMoMSwxLDEpLCBsd2QgPSByZXAoMSwzKSwKICAgICAgIGNvbCA9IGMoInJlZCIsICJibHVlIiwgIm9yYW5nZSIpLAogICAgICAgYnR5PSJuIixjZXggPSAwLjgpCiMjIGFubm90YXRpb24gLSBBVUMKdGV4dCgwLjgsIDAuNDYsIHBhc3RlKCJMb2dpc3RpYyBBVUM6ICIsIHJvdW5kKGF1Yy5sb2dpdCw0KSksIGNleCA9IDAuOCkKdGV4dCgwLjgsIDAuNCwgcGFzdGUoIlRyZWUgMVNFIEFVQzogIiwgcm91bmQoYXVjLnRyZWUuMVNFLDQpKSwgY2V4ID0gMC44KQp0ZXh0KDAuOCwgMC4zNCwgcGFzdGUoIlRyZWUgTWluIEFVQzogIiwgcm91bmQoYXVjLnRyZWUubWluLDQpKSwgY2V4ID0gMC44KQoKYGBgCgojIyBPcHRpbWFsIEN1dG9mZgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGUgYWJvdmUgcGxvdCBzaG93cyB0aGUgb3B0aW1hbCBjdXRvZmYgdG8gYmUgMC4zODg5LiBUaGlzIHRocmVzaG9sZCBpcyB1c2VkIHRvIG1pbmltaXplIG1pc3NjbGFzc2lmaWNhdGlvbi4nfQojIHByZWRpdGl2ZSBwcm9iYWJpbGl0aWVzIG9mIHRyZWUubWluIG1vZGVsLgpwcmVkLnByb2IubWluIDwtIHByZWRpY3QocHJ1bmVkLnRyZWUubWluLCB0cmFpbi5kYXRhLCB0eXBlID0gInByb2IiKVssMl0KIyMKY29zdCA8LSBOVUxMCmN1dG9mZiA8LXNlcSgwLDEsIGxlbmd0aCA9IDEwKQojIwpmb3IgKGkgaW4gMToxMCl7CiAgcHJlZC5sYWJlbCA8LSBpZmVsc2UocHJlZC5wcm9iLm1pbiA+IGN1dG9mZltpXSwgIllFUyIsICJOTyIpCiAgRk4gPC0gc3VtKHByZWQubGFiZWwgPT0gIk5PIiAmIHRyYWluLmRhdGEkTFVOR19DQU5DRVIgPT0gIllFUyIpCiAgRlAgPC0gc3VtKHByZWQubGFiZWwgPT0gIllFUyIgJiB0cmFpbi5kYXRhJExVTkdfQ0FOQ0VSID09ICJOTyIpCiAgY29zdFtpXSA9IDUqRlAgKyAyMCpGTgp9CiMjIGlkZW50aWZ5IG9wdGltYWwgY3V0LW9mZgptaW4uSUQgPC0gd2hpY2goY29zdCA9PSBtaW4oY29zdCkpICAgIyBjb3VsZCBoYXZlIG11bHRpcGxlIG1pbmltdW0Kb3B0aW0ucHJvYiA8LSBtZWFuKGN1dG9mZlttaW4uSURdKSAgICMgdGFrZSB0aGUgYXZlcmFnZSBvZiB0aGUgY3V0LW9mZnMKIyMKcGxvdChjdXRvZmYsIGNvc3QsIHR5cGUgPSAiYiIsIGNvbCA9ICJuYXZ5IiwKICAgICBtYWluID0gIkN1dG9mZiB2cyBNaXNjbGFzc2lmaWNhdGlvbiBDb3N0IikKIyMKdGV4dCgwLjIsIDM1MDAsIHBhc3RlKCJPcHRpbWFsIGN1dG9mZjoiLCByb3VuZChvcHRpbS5wcm9iLDQpKSwgY2V4ID0gMC44KQoKYGBgCgojIyBDb25jbHVzaW9uCgpUaGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBwZXJmb3JtcyBzdXBlcmlvciB0byB0aGUgc3VnZ2VzdGVkIENBUlQgQ2xhc3NpZmljYXRpb24gbW9kZWxzLiBIb3dldmVyLCB0aGUgQ0FSVCBtb2RlbHMgc2hvd2VkIHRoYXQgZGlmZmljdWx0eSBicmVhdGhpbmcgd2FzIGEga2V5IHByZWRpY3RvciBpbiBsdW5nIGNhbmNlci4gT3RoZXIga2V5IHByZWRpY3RvcnMgd2VyZSBhbGNvaG9sIGNvbnN1bXB0aW9uLCB3aGVlemluZywgYW5kIGFsbGVyZ3kuIA==